              package com.java.diagnostics.visualizer.displayer.html;
              
              import com.java.diagnostics.visualizer.coredisplayers.util.Messages;
              import com.java.diagnostics.visualizer.data.AggregateData;
              import com.java.diagnostics.visualizer.data.DataPoint;
              import com.java.diagnostics.visualizer.data.DataSet;
              import com.java.diagnostics.visualizer.data.TupleData;
              import com.java.diagnostics.visualizer.data.Variant;
              import com.java.diagnostics.visualizer.data.ids.ID;
              import com.java.diagnostics.visualizer.properties.OutputProperties;

              import java.io.BufferedWriter;
              import java.io.File;
              import java.io.IOException;
              import java.util.HashMap;
              import java.util.Iterator;
              import java.util.Map;
              import java.util.Set;
              import java.util.TreeMap;
              import java.util.TreeSet;
              
              public class FrequencyTableTupleDataWriter extends TupleDataWriter
              {
                private static final String TOTAL_INSTANCES = Messages.getString("FrequencyTableTupleDataWriter.total.instances");
                static final String OOM_GARBAGE_COLLECT = "OOM GC";
                static final String OOM_GARBAGE_COLLECT_LABEL = Messages.getString("FrequencyTableTupleDataWriter.oom.synch.gc");
              
                static final String FORCED_SYNCHGC_LABEL = Messages.getString("FrequencyTableTupleDataWriter.forced.synch.gc");
              
                private Map<TupleData, Map<String, Integer>> tupleStats = new HashMap();
                private Set<String> valueNames = new TreeSet();
                private String units = null;
              
                protected FrequencyTableTupleDataWriter(DataSet dataSet, OutputProperties outputProperties, AggregateData representativeData, BufferedWriter buffer, ID id, File directory)
                {
                  super(dataSet, outputProperties, representativeData, buffer, id, 
                    directory);
              
                  for (Variant v : dataSet.getVariants()) {
                    TupleData tuple = v.getTupleData(id);
                    if ((tuple != null) && (!tuple.isEmpty())) {
                      this.units = outputProperties.getYUnits(tuple);
                      Map namesToCounts = getValueTable(tuple);
                      this.valueNames.addAll(namesToCounts.keySet());
                      this.tupleStats.put(tuple, namesToCounts);
                    }
                  }
                }
              
                private Map<String, Integer> getValueTable(TupleData tuple) {
                  DataPoint[] dataPoints = tuple.getDataPoints();
                  Map namesToCounts = new TreeMap();
                  String yUnits = this.outputProperties.getYUnits(tuple);
              
                  for (DataPoint dp : dataPoints) {
                    String value = dp.formatY(yUnits);
                    if (namesToCounts.get(value) == null)
                      namesToCounts.put(value, Integer.valueOf(1));
                    else {
                      namesToCounts.put(value, Integer.valueOf(((Integer)namesToCounts.get(value)).intValue() + 1));
                    }
                  }
              
                  return namesToCounts;
                }
              
                public void writeData() throws IOException
                {
                  boolean showTupleStats = this.preferencesHelper.getShowTupleStats();
              
                  this.usingTable = true;
                  addDataHeading();
              
                  if (showTupleStats) {
                    openTable();
              
                    if (this.usingTable) {
                      writeColumnHeadings();
                    }
              
                    Variant[] variants = this.dataSet.getVariants();
                    for (int i = 0; i < variants.length; i++)
                    {
                      openRow();
                      if (i == 0) {
                        addTableHeader(TOTAL_INSTANCES, 
                          this.dataSet.getVariants().length);
                      }
                      Variant variant = variants[i];
              
                      if (this.dataSet.getVariants().length > 1) {
                        addTableHeader(variant.getLabel(), 1);
                      }
                      TupleData data = variant.getTupleData(this.id);
                      addRow(data);
              
                      closeRow();
                      this.oddRow = (!this.oddRow);
                    }
                    closeTable();
                  }
                  createImage(this.dataSet, this.outputProperties, this.id);
                }
              
                protected void writeColumnHeadings() throws IOException
                {
                  this.buffer.write(NEWLINE);
                  openRow();
                  addTableHeader(this.units, 1);
                  if (this.dataSet.getVariants().length > 1) {
                    addTableHeader(VARIANT, 1);
                  }
              
                  for (String headerString : this.valueNames)
                  {
                    if (headerString.equals("synchgc"))
                      addTableHeader(FORCED_SYNCHGC_LABEL, 1);
                    else if (headerString.equalsIgnoreCase("OOM GC"))
                      addTableHeader(OOM_GARBAGE_COLLECT_LABEL, 1);
                    else {
                      addTableHeader(headerString, 1);
                    }
              
                  }
              
                  closeRow();
                  this.oddRow = false;
                }
              
                protected void addRow(TupleData data)
                  throws IOException
                {
                  Map valuesToNames;
                  if (data != null) {
                    this.buffer.write(NEWLINE);
                    valuesToNames = (Map)this.tupleStats.get(data);
                    for (String headerString : this.valueNames) {
                      Integer value = (Integer)valuesToNames.get(headerString);
                      String valueStr;
                      //String valueStr;
                      if (value != null)
                        valueStr = value.toString();
                      else {
                        valueStr = Integer.toString(0);
                      }
                      addTableCell(valueStr);
                    }
              
                    this.buffer.write(NEWLINE);
                  }
                  else {
                    this.buffer.write(NEWLINE);
                    for (valuesToNames = (Map) this.valueNames.iterator(); ((Iterator<String>) valuesToNames).hasNext(); )
                  { 
	               // deleted by wenguang.huang 
	               //((String)valuesToNames.next());
                      addTableCell("&nbsp;");
                    }
              
                    this.buffer.write(NEWLINE);
                  }
                }
              }

